// Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// issues on https://gitlab.onelab.info/gmsh/gmsh/issues.

#ifndef GMODELIO_GEO_H
#define GMODELIO_GEO_H

class GModel;
class ExtrudeParams;
class gmshSurface;
class List_T;
class Tree_T;

class GEO_Internals {
public:
    // this will become private
    Tree_T *Points, *Curves, *EdgeLoops, *Surfaces, *SurfaceLoops, *Volumes;
    Tree_T *DelPoints, *DelCurves, *DelSurfaces, *DelVolumes;
    List_T *PhysicalGroups, *DelPhysicalGroups;

private:
    std::multimap<int, std::vector<int> > _meshCompounds;
    int _maxPointNum, _maxLineNum, _maxLineLoopNum, _maxSurfaceNum;
    int _maxSurfaceLoopNum, _maxVolumeNum, _maxPhysicalNum;
    void _allocateAll();
    void _freeAll();
    bool _changed;
    bool _transform(int mode, const std::vector<std::pair<int, int> > &dimTags,
                    double x, double y, double z, double dx, double dy, double dz,
                    double a, double b, double c, double d);
    bool _extrude(int mode, const std::vector<std::pair<int, int> > &inDimTags,
                  double x, double y, double z, double dx, double dy, double dz,
                  double ax, double ay, double az, double angle,
                  std::vector<std::pair<int, int> > &outDimTags,
                  ExtrudeParams *e = 0);

public:
    GEO_Internals() { _allocateAll(); }
    ~GEO_Internals() { _freeAll(); }
    void destroy()
    {
        _freeAll();
        _allocateAll();
    }

    // have the internals changed since the last synchronisation?
    bool getChanged() const { return _changed; }

    // set/get max tag of entity for each dimension (0, 1, 2, 3), as well as
    // -2 for shells and -1 for wires
    void setMaxTag(int dim, int val);
    int getMaxTag(int dim) const;

    // add shapes (if tag is < 0, a new tag is automatically created and returned)
    bool addVertex(int &tag, double x, double y, double z, double lc);
    bool addVertex(int &tag, double x, double y, gmshSurface *s, double lc);
    bool addLine(int &tag, int startTag, int endTag);
    bool addLine(int &tag, const std::vector<int> &pointTags);
    bool addCircleArc(int &tag, int startTag, int centerTag, int endTag,
                      double nx = 0., double ny = 0., double nz = 0.);
    bool addEllipseArc(int &tag, int startTag, int centerTag, int majorTag,
                       int endTag, double nx = 0., double ny = 0.,
                       double nz = 0.);
    bool addSpline(int &tag, const std::vector<int> &pointTags);
    bool addBezier(int &tag, const std::vector<int> &pointTags);
    bool addBSpline(int &tag, const std::vector<int> &pointTags,
                    const std::vector<double> &seqknots = std::vector<double>());
    bool addLineLoop(int &tag, const std::vector<int> &curveTags);
    bool addPlaneSurface(int &tag, const std::vector<int> &wireTags);
    bool addDiscreteSurface(int &tag);
    bool addSurfaceFilling(int &tag, const std::vector<int> &wireTags,
                           int sphereCenterTag = -1);
    bool addSurfaceLoop(int &tag, const std::vector<int> &surfaceTags);
    bool addVolume(int &tag, const std::vector<int> &shellTags);

    // extrude and revolve
    bool extrude(const std::vector<std::pair<int, int> > &inDimTags, double dx,
                 double dy, double dz,
                 std::vector<std::pair<int, int> > &outDimTags,
                 ExtrudeParams *e = 0);
    bool revolve(const std::vector<std::pair<int, int> > &inDimTags, double x,
                 double y, double z, double ax, double ay, double az,
                 double angle, std::vector<std::pair<int, int> > &outDimTags,
                 ExtrudeParams *e = 0);
    bool twist(const std::vector<std::pair<int, int> > &inDimTags, double x,
               double y, double z, double dx, double dy, double dz, double ax,
               double ay, double az, double angle,
               std::vector<std::pair<int, int> > &outDimTags,
               ExtrudeParams *e = 0);
    bool boundaryLayer(const std::vector<std::pair<int, int> > &inDimTags,
                       std::vector<std::pair<int, int> > &outDimTags,
                       ExtrudeParams *e = 0);

    // apply transformations
    bool translate(const std::vector<std::pair<int, int> > &dimTags, double dx,
                   double dy, double dz);
    bool rotate(const std::vector<std::pair<int, int> > &dimTags, double x,
                double y, double z, double ax, double ay, double az,
                double angle);
    bool dilate(const std::vector<std::pair<int, int> > &dimTags, double x,
                double y, double z, double a, double b, double c);
    bool symmetry(const std::vector<std::pair<int, int> > &dimTags, double a,
                  double b, double c, double d);

    // split entities
    bool splitCurve(int tag, const std::vector<int> &pointTags,
                    std::vector<int> &curveTags);
    bool intersectCurvesWithSurface(const std::vector<int> &curveTags,
                                    int surfaceTag, std::vector<int> &pointTags);

    // copy and remove
    bool copy(const std::vector<std::pair<int, int> > &inDimTags,
              std::vector<std::pair<int, int> > &outDimTags);
    bool remove(int dim, int tag, bool recursive = false);
    bool remove(const std::vector<std::pair<int, int> > &dimTags,
                bool recursive = false);

    // manipulate physical groups
    void resetPhysicalGroups();
    bool modifyPhysicalGroup(int dim, int tag, int op,
                             const std::vector<int> &tags);
    int getMaxPhysicalTag() const { return _maxPhysicalNum; }
    void setMaxPhysicalTag(int val) { _maxPhysicalNum = val; }

    // coherence
    void removeAllDuplicates();
    bool mergeVertices(const std::vector<int> &tags);

    // set meshing constraints
    void setCompoundMesh(int dim, const std::vector<int> &tags);
    void setMeshSize(int dim, int tag, double size);
    void setDegenerated(int dim, int tag);
    void setTransfiniteLine(int tag, int nPoints, int type, double coef);
    void setTransfiniteSurface(int tag, int arrangement,
                               const std::vector<int> &cornerTags);
    void setTransfiniteVolume(int tag, const std::vector<int> &cornerTags);
    void setTransfiniteVolumeQuadTri(int tag);
    void setRecombine(int dim, int tag, double angle);
    void setSmoothing(int tag, int val);
    void setReverseMesh(int dim, int tag, bool val = 1);

    // synchronize internal CAD data with the given GModel
    void synchronize(GModel *model);

    // queries
    bool getVertex(int tag, double &x, double &y, double &z);

    // create coordinate systems
    gmshSurface *newGeometrySphere(int tag, int centerTag, int pointTag);
    gmshSurface *newGeometryPolarSphere(int tag, int centerTag, int pointTag);
};

#endif
